/***********************************************
This file is part of the AMonSys project (http://code.google.com/p/amonsys/).

    AMonSys is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    AMonSys is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with AMonSys.  If not, see <http://www.gnu.org/licenses/>.
**********************************************/
package main

import(
	"flag";
	"fmt";
	"net";
	"os";
	"http";
	"io";
	"template";
	"time";
	"strconv";
	"strings";
	"bytes";
	"io/ioutil";
	"syscall"
	"./config"
	"./asterisk"
)

var(
	Astcfg		config.AstConfig;
	ApmsConf	config.ApmsConfig;
	ApmsConn	*net.TCPConn;
	ApmsBuf		[512]byte;
	MainPeers	string;
	MainQueue	string;
	MainQPeer	string;
	ACh		int;
)

func main(){
	var confdir *string = flag.String("confdir", "/etc/amonsys/", "Set the location of configuration files.");
	var uid *int = flag.Int("uid", 0, "Set the UID of amonsys user.");
	flag.Parse();

	switch flag.Arg(0) {
		case "v","version":
			fmt.Print("Asterisk Monitoring System v0.1.0 \n")
			os.Exit(2)
	}

	Astcfg, ApmsConf = config.LoadConf(string(*confdir));
	ApmsConf.ConfDir = string(*confdir);

	if os.Args[0] == "01" {
		srv, err := net.ResolveTCPAddr(Astcfg.Srv+":"+Astcfg.Port);
		cli, err := net.ResolveTCPAddr(Astcfg.Srv);
		
		ApmsConn, err = net.DialTCP("tcp", cli, srv);
		if err != nil {
				error := config.LogAsterisk(err.String(),ApmsConf.LogFolder)
				fmt.Print(error)
		} else {
			ACh = 0;
			res := asterisk.ApmsLogin(ApmsConn,&ApmsBuf,Astcfg.User,Astcfg.Pass,ApmsConf);
			if res == false {
				error := config.LogAsterisk("failed to authenticate as '"+Astcfg.User+"'",ApmsConf.LogFolder);
				fmt.Print(error);
				os.Exit(1)
			}
			http.Handle("/", http.HandlerFunc(HttpHome))
			http.Handle("/login", http.HandlerFunc(HttpLogin))
			http.Handle("/logout", http.HandlerFunc(HttpLogout))
			http.Handle("/peers/", http.HandlerFunc(HttpPeers))
			http.Handle("/core/", http.HandlerFunc(HttpCheck))
			http.Handle("/queue/", http.HandlerFunc(HttpQueue))
			http.Handle("/queuepeer/", http.HandlerFunc(HttpQueuePeer))
			http.Handle("/monitor/", http.HandlerFunc(HttpPeersMonitor))
			err := http.ListenAndServe("127.0.0.1:1741", nil);
			if err != nil {
				error := config.LogConnection(err.String(),ApmsConf.LogFolder)
				fmt.Print(error)
				os.Exit(1);
			}
			asterisk.ApmsLogout(ApmsConn,&ApmsBuf);
		}
	} else {
		localAddr := ApmsConf.HttpIP+":"+ApmsConf.HttpPort;
		remoteAddr := "127.0.0.1:1741"
		local, err := net.Listen("tcp", localAddr)
		if local == nil {
			error := config.LogError(err.String(),ApmsConf.LogFolder)
			fmt.Print(error)
			os.Exit(2)
		}
		_ = syscall.Setuid(int(*uid));
		pid,_ := syscall.ForkExec(os.Args[0],[]string{"01","-confdir="+ApmsConf.ConfDir},[]string{""},"",[]int{0});
		for {
			go func(){
				os.Wait(pid,0)
				os.Exit(1)
			}()
			conn, err := local.Accept()
			if conn == nil {
				error := config.LogConnection(err.String(),ApmsConf.LogFolder)
				fmt.Print(error)
				os.Exit(2)
			}
			go forward(conn, remoteAddr)
		}
	}
}


func forward(local net.Conn, remoteAddr string) {
        remote, err := net.Dial("tcp", "", remoteAddr)
        if remote == nil {
		error := config.LogConnection(err.String(),ApmsConf.LogFolder)
		fmt.Print(error)
                return
        }
        go io.Copy(local, remote)
        go io.Copy(remote, local)
}

func qCore(){
	if ACh == 0 {
		ACh++;
		mainCore();
	}
}

func mainCore() {
		c := make(chan int);
		for ACh > 0 {
			go func(){
				MainPeers,MainQueue,MainQPeer = asterisk.MainMonitor(ApmsConn,&ApmsBuf,Astcfg,ApmsConf);
				c <- 1;
			}()
			<- c;
			ACh--;
		}
}

func HttpHome(c *http.Conn, req *http.Request) {
	var (
		lang		map[string]string;
		value		[]string;
		present		bool;
	)
	req.ParseForm();
	value,present = req.Form["hl"];
	if present == true {
		lang = config.SetLanguage(value[0],ApmsConf);
		lang["hl"] = value[0],true;
	} else {
		lang = config.SetLanguage(ApmsConf.LangDefault,ApmsConf);
		lang["hl"] = ApmsConf.LangDefault,true;
	}

	file := ApmsConf.HttpDir+"/"+req.URL.Path[1:];
	if file[(len(file)-1):len(file)] == "/"{
		file = file+"index.html";
	}

	switch file[(len(file)-4):len(file)] {
		case "html",".htm":
			t, err := template.ParseFile(file, nil);
			if err != nil {
				io.WriteString(c,"404 File Not Found");
			} else {
				t.Execute(lang, c);
			}
		default:
			http.ServeFile(c, req, file);
	}
}

func HttpLogin(c *http.Conn, req *http.Request) {
	var (
		lang		map[string]string;
		value		[]string;
		present		bool;
	)
	if req.Method == "POST" {
		req.ParseForm();
		value,present = req.Form["hl"];
		if present == true {
			lang = config.SetLanguage(value[0],ApmsConf);
			lang["hl"] = value[0],true;
		} else {
			lang = config.SetLanguage(ApmsConf.LangDefault,ApmsConf);
			lang["hl"] = ApmsConf.LangDefault,true;
		}
		res,id,admin,err := checkLogin(req.Form["sysUsr"][0],req.Form["sysPwd"][0]);
		if res == false && err == nil {
			result := "<script type=\"text/javascript\">\n";
			result = result + "parent.createBox('"+id+"','{loginOk}');\n";
			result = result + "</script>";
			t, _ := template.Parse(result, nil);
			t.Execute(lang, c);
		}
		if res == true {
			cID := id+strconv.Itoa64(time.Seconds());
			result := "<script type=\"text/javascript\">\n";
			result = result + "document.cookie = 'amonsyscid="+cID+";expires="+time.SecondsToUTC(time.Seconds()+(60*15)).String()+";path=/'\n";
			result = result + "document.cookie = 'amonsysadm="+strconv.Itoa(admin)+";expires="+time.SecondsToUTC(time.Seconds()+(60*15)).String()+";path=/'\n";
			result = result + "parent.location.href=\"/peers/?hl={hl}\";\n";
			result = result + "</script>";
			t, _ := template.Parse(result, nil);
			t.Execute(lang, c);
		}
		if err != nil {
			result := "<script type=\"text/javascript\">\n";
			result = result + "parent.createBox('"+err.String()+"','{loginOk}');\n";
			result = result + "</script>";
			t, _ := template.Parse(result, nil);
			t.Execute(lang, c);
		}
	} else {
		c.SetHeader("Location","/");
		c.WriteHeader(307);
	}
}

func HttpLogout(c *http.Conn, req *http.Request) {
	var (
		lang		map[string]string;
		value		[]string;
		present		bool;
	)
	req.ParseForm();
	cookie := HttpCookies(req);
	value,present = req.Form["hl"];
	if present == true {
		lang = config.SetLanguage(value[0],ApmsConf);
		lang["hl"] = value[0],true;
	} else {
		lang = config.SetLanguage(ApmsConf.LangDefault,ApmsConf);
		lang["hl"] = ApmsConf.LangDefault,true;
	}

	result := "<script type=\"text/javascript\">\n";
	result = result + "document.cookie = 'amonsyscid="+cookie["amonsyscid"]+";expires="+time.SecondsToUTC(time.Seconds()-(60*30)).String()+";path=/'\n";
	result = result + "document.cookie = 'amonsysadm="+cookie["amonsysadm"]+";expires="+time.SecondsToUTC(time.Seconds()-(60*30)).String()+";path=/'\n";
	result = result + "parent.location.href=\"/\";\n";
	result = result + "</script>";
	t, _ := template.Parse(result, nil);
	t.Execute(lang, c);

	c.SetHeader("Location","/");
	c.WriteHeader(307);
}

func HttpPeers(c *http.Conn, req *http.Request) {
	var (
		lang		map[string]string;
		value		[]string;
		present		bool;
	)
	cookie := HttpCookies(req);
	v,p := cookie["amonsyscid"];
	if p == true && len(v) == 42 {
		req.ParseForm();
		value,present = req.Form["hl"];
		if present == true {
			lang = config.SetLanguage(value[0],ApmsConf);
			lang["hl"] = value[0],true;
		} else {
			lang = config.SetLanguage(ApmsConf.LangDefault,ApmsConf);
			lang["hl"] = ApmsConf.LangDefault,true;
		}
		file := ApmsConf.HttpDir+"/"+req.URL.Path[1:];
		if file[(len(file)-1):len(file)] == "/"{
			file = file+"index.html";
		}
		switch file[(len(file)-4):len(file)] {
			case "html",".htm":
				t, err := template.ParseFile(file, nil);
				if err != nil {
					io.WriteString(c,"404 File Not Found");
				} else {
					t.Execute(lang, c);
				}
			default:
				http.ServeFile(c, req, file);
		}
	} else {
		c.SetHeader("Location","/");
		c.WriteHeader(307);
	}
}

func HttpCheck(c *http.Conn, req *http.Request) {
	var (v string; p bool;)
	cookie := HttpCookies(req);
	v,p = cookie["amonsyscid"];
	if p == true && len(v) == 42 {
			qCore();
			result := cookie["amonsyscid"]+";"+cookie["amonsysadm"]+";"+time.SecondsToUTC(time.Seconds()+(60*15)).String();
			io.WriteString(c,result);
	} else {
		c.SetHeader("Location","/");
		c.WriteHeader(307);
	}
}

func HttpQueue(c *http.Conn, req *http.Request) {
	var (v string; p bool;)
	cookie := HttpCookies(req);
	v,p = cookie["amonsyscid"];
	if p == true && len(v) == 42 {
		io.WriteString(c, MainQueue);
	} else {
		c.SetHeader("Location","/");
		c.WriteHeader(307);
	}
}

func HttpQueuePeer(c *http.Conn, req *http.Request) {
	var (v string; p bool;)
	cookie := HttpCookies(req);
	v,p = cookie["amonsyscid"];
	if p == true && len(v) == 42 {
		io.WriteString(c, MainQPeer);
	} else {
		c.SetHeader("Location","/");
		c.WriteHeader(307);
	}
}

func HttpPeersMonitor(c *http.Conn, req *http.Request) {
	var (v string; p bool;)
	cookie := HttpCookies(req);
	v,p = cookie["amonsyscid"];
	if p == true && len(v) == 42 {
			io.WriteString(c, MainPeers);
	} else {
		c.SetHeader("Location","/");
		c.WriteHeader(307);
	}
}


func HttpCookies(req *http.Request) map[string]string {
	cookie := make(map[string]string);
	mc := strings.Split(req.Header["Cookie"],";",-1);
	for i:=0; i<len(mc);i++ {
		ck := strings.TrimLeft(mc[i]," ");
		cook := strings.Split(ck,"=",-1);
		if len(cook) == 2 {
			cookie[cook[0]] = cook[1],true;
		}
	}
	return cookie;
}

func checkLogin(usr string, pass string) (bool,string,int,os.Error) {
	filename := strings.TrimRight(ApmsConf.ConfDir,"/")+"/users";
	file, err := ioutil.ReadFile(filename);
	if err != nil {
		return false,"",0,err
	}
	nfile := bytes.Split(file,[]byte("\n"),-1);
	for i:=0; i<len(nfile); i++{
		data := strings.Split(string(nfile[i]),"#",-1);
		if data[0] != "" {
			line := strings.Split(data[0],":",-1);
			if len(line) == 5 {
				if line[0] == usr && line[2] == pass {
					if line[4] == "1" {
						admin,_ := strconv.Atoi(line[3]);
						return true,line[1],admin,nil
					} else {
						admin,_ := strconv.Atoi(line[3]);
						return false,"{loginDisabled}",admin,nil
					}
				}
			}
		}
	}
	return false,"{loginError}",0,nil
}
